#ifndef COLUMN_H
#define COLUMN_H

#include <string>
#include <optional>
#include <regex>
#include <variant>
#include <type_traits>

class Value {
private:
    std::variant<std::monostate, int, double, std::string> value;

public:
    Value() : value(std::monostate{}) {}
    Value(int val) : value(val) {}
    Value(double val) : value(val) {}
    Value(const std::string& val) : value(val) {}
    Value(const char* val) : value(std::string(val)) {}

    template<typename T>
    bool is() const {
        return std::holds_alternative<T>(value);
    }

    template<typename T>
    T get() const {
        return std::get<T>(value);
    }

    std::string toString() const {
        if (is<int>()) {
            return std::to_string(get<int>());
        }
        else if (is<double>()) {
            std::string doubleStr = std::to_string(get<double>());
            doubleStr.erase(doubleStr.find_last_not_of('0') + 1, std::string::npos);
            if (doubleStr.back() == '.') {
                doubleStr.pop_back();
            }
            return doubleStr;
        }
        else if (is<std::string>()) {
            return get<std::string>();
        }
        else {
            return "";
        }
    }

    bool compare(const std::string& otherStr) const {
        return toString() == otherStr;
    }
};

class Column {
protected:
    std::string name;
    std::optional<Value> defaultValue;
    bool nullable = false;
    bool indexed = false;
    bool unique = false;

public:
    Column(const std::string& colName) : name(colName) {}
    virtual ~Column() = default;

    const std::string& getName() const { return name; }
    virtual std::string getType() const = 0;

    void setDefaultValue(const Value& defValue) {
        if (checkConstraint(defValue)) {
            defaultValue = defValue;
        }
        else {
            throw std::invalid_argument("Invalid default value");
        }
    }

    std::optional<Value> getDefaultValue() const {
        return defaultValue;
    }

    virtual bool checkConstraint(const Value& val) const = 0;

    void setNullable(bool isNullable) { nullable = isNullable; }
    bool isNullable() const { return nullable; }

    void setIndexed(bool isIndexed) { indexed = isIndexed; }
    bool isIndexed() const { return indexed; }

    void setUnique(bool isUnique) { unique = isUnique; }
    bool isUnique() const { return unique; }
};

class IntColumn : public Column {
private:
    std::optional<int> lowerBound;
    std::optional<int> upperBound;

public:
    IntColumn(const std::string& colName) : Column(colName) {}

    void setLowerBound(int bound) { lowerBound = bound; }
    void setUpperBound(int bound) { upperBound = bound; }

    std::optional<int> getLowerBound() const { return lowerBound; }
    std::optional<int> getUpperBound() const { return upperBound; }

    std::string getType() const override { return "INT"; }

    bool checkConstraint(const Value& val) const override {
        if (val.is<std::monostate>()) {
            return nullable;
        }
        if (val.is<int>()) {
            int intVal = val.get<int>();
            if (lowerBound && intVal < *lowerBound) return false;
            if (upperBound && intVal > *upperBound) return false;
            return true;
        }
        return false;
    }
};

class DoubleColumn : public Column {
private:
    std::optional<double> lowerBound;
    std::optional<double> upperBound;

public:
    DoubleColumn(const std::string& colName) : Column(colName) {}

    void setLowerBound(double bound) { lowerBound = bound; }
    void setUpperBound(double bound) { upperBound = bound; }

    std::optional<double> getLowerBound() const { return lowerBound; }
    std::optional<double> getUpperBound() const { return upperBound; }

    std::string getType() const override { return "DOUBLE"; }

    bool checkConstraint(const Value& val) const override {
        if (val.is<std::monostate>()) {
            return nullable;
        }
        if (val.is<double>()) {
            double doubleVal = val.get<double>();
            if (lowerBound && doubleVal < *lowerBound) return false;
            if (upperBound && doubleVal > *upperBound) return false;
            return true;
        }
        return false;
    }
};

class StringColumn : public Column {
private:
    std::optional<size_t> maxLength;

public:
    StringColumn(const std::string& colName) : Column(colName) {}

    void setMaxLength(size_t length) { maxLength = length; }

    std::optional<size_t> getMaxLength() const { return maxLength; }

    std::string getType() const override { return "STRING"; }

    bool checkConstraint(const Value& val) const override {
        if (val.is<std::monostate>()) {
            return nullable;
        }
        if (val.is<std::string>()) {
            const std::string& strVal = val.get<std::string>();
            if (maxLength && strVal.length() > *maxLength) return false;
            return true;
        }
        return false;
    }
};

#endif // COLUMN_H